﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Monitor.Core.Sensor
{
    class MongoShardSensor : ICompositeSensor
    {
        private MongoReplicaSensor _config;
        private List<ISensor> _replicas;
        private Dictionary<ISensor, ServiceStatus> _shardStatus;

        private int _id;
        private string _description;
        private int _interval;
        private DateTime _lastRefreshTime;
        private ServiceStatus _status;

        public MongoShardSensor(int id)
        {
            _id = id;
            _replicas = new List<ISensor>();
            _shardStatus = new Dictionary<ISensor, ServiceStatus>();
            foreach (var s in _replicas)
            {
                _shardStatus[s] = ServiceStatus.Unknown;
            }
        }

        public void AddComponent(ISensor sensor)
        {
            var cfg = sensor as MongoConfigReplicaSensor;
            if (cfg != null)
                this.AddComponent(cfg);
            else
                this.AddComponent(sensor as MongoReplicaSensor);
        }

        public void AddComponent(MongoReplicaSensor sensor)
        {
            _replicas.Add(sensor);
            _shardStatus[sensor] = ServiceStatus.Unknown;
        }

        public void AddComponent(MongoConfigReplicaSensor config)
        {
            _config = config;
        }

        public int Id
        {
            get { return _id; }
        }

        public string Description
        {
            get
            {
                return _description;
            }
            set
            {
                _description = value;
            }
        }

        public int Interval
        {
            get
            {
                return _interval;
            }
            set
            {
                _interval = value;
            }
        }

        public ServiceStatus Status
        {
            get { return _status; }
        }

        public string LastError
        {
            get
            {
                StringBuilder str = new StringBuilder();
                if (_config.Status != ServiceStatus.Alive)
                    str.Append(_config.Description + ": " + _config.LastError + "\n");
                foreach (var s in _replicas)
                {
                    if (s.Status != ServiceStatus.Alive)
                    {
                        str.Append(s.Description + ": " + s.LastError + '\n');
                    }
                }
                return str.ToString();
            }
        }

        public DateTime LastRefreshTime
        {
            get
            {
                return _lastRefreshTime;
            }
        }

        public ServiceStatus Ping()
        {
            _lastRefreshTime = DateTime.Now;

            var cfgStatus = PingConfigService();
            var shardStatus = PingShards();
            if (cfgStatus == ServiceStatus.Alive && shardStatus == ServiceStatus.Alive)
            {
                _status = ServiceStatus.Alive;
                return _status;
            }

            if (cfgStatus == ServiceStatus.Dead || shardStatus == ServiceStatus.Dead)
            {
                _status = ServiceStatus.Dead;
                return _status;
            }
            else
            {
                _status = ServiceStatus.Unstable;
                return _status;
            }
        }

        private ServiceStatus PingShards()
        {
            Parallel.ForEach<ISensor>(_replicas, (s) =>
            {
                _shardStatus[s] = s.Ping();
            });

            int alive = _shardStatus.Values.Count(x => x == ServiceStatus.Alive);
            if (alive == _replicas.Count())
                return ServiceStatus.Alive;
            int unstable = _shardStatus.Values.Count(x => x == ServiceStatus.Unstable);
            if (alive + unstable == _replicas.Count())
                return ServiceStatus.Unstable;
            return ServiceStatus.Dead;
        }

        private ServiceStatus PingConfigService()
        {
            return _config.Ping();
        }
    }
}
